﻿using Caliburn.Micro;
using MusicExplorer.Common;
using MusicExplorer.Data;
using MusicExplorer.Model;
using MusicExplorer.Repository;
using MusicExplorer.ViewModels.Wrappers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using MusicExplorer.Tools;
using MusicExplorer.Extensions;
using MusicExplorer.Repository.SearchEngine;
using System.Diagnostics;
using Windows.UI.Core;

namespace MusicExplorer.ViewModels
{
    public sealed class GroupEditionViewModel : PageViewModelBase
    {
        private QueryGroup m_queryGroup;
        private readonly DispatcherTimer m_searchTimer;
        private bool m_inSearch;

        private EntitiesCollection m_entities;
        private readonly BindableCollection<EntityResultViewModel> m_selectedEntities;
        private readonly BindableCollection<string> m_entityTypes;
        private readonly BindableCollection<string> m_searchedImages;

        private readonly CoreDispatcher m_dispatcher;

        public GroupEditionViewModel(INavigationService navigationService)
            : base(navigationService)
        {
            m_dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
            
            m_selectedEntities = new BindableCollection<EntityResultViewModel>();
            m_entityTypes = new BindableCollection<string>();
            m_searchedImages = new BindableCollection<string>();

            m_imageKeyboardTimer = new DispatcherTimer();
            m_keyboardTimer = new DispatcherTimer();
            m_inSearch = false;
            m_searchInProgress = false;
            m_searchTerms = string.Empty;
            m_imageSearchTerms = string.Empty;

            m_searchTimer = new DispatcherTimer();
            m_searchTimer.Interval = TimeSpan.FromMilliseconds(100);
            m_searchTimer.Tick += (s, e) =>
            {
                m_searchTimer.Stop();
                if (!m_inSearch) SearchInProgress = false;
            };
        }

        protected async override void OnInitialize()
        {
            ShowLoading();

            m_queryGroup = ((GroupEditionNavigationObject)Parameter).Group;
            Name = m_queryGroup.Name;
            //m_displayOnHomeScreen = m_queryGroup.DisplayNameOnHomeScreen;
            //NotifyOfPropertyChange(() => DisplayOnHomeScreen);
            m_selectedQueryType = m_queryGroup.QueryType;
            NotifyOfPropertyChange(() => SelectedQueryType);
            RefreshEntityTypes();

            if (m_queryGroup.Image != null)
            {
                m_image = new BitmapImage();
                m_image.DecodePixelHeight = 170;
                await m_image.FillWithBytes(m_queryGroup.Image);
                m_imageBytes = m_queryGroup.Image;
                NotifyOfPropertyChange(() => Image);
            }

            if (m_queryGroup.Entities != null && m_queryGroup.Entities.Count > 0)
            {
                foreach (var entity in m_queryGroup.Entities)
                {
                    m_selectedEntities.Add(await EntityResultViewModel.CreateAsync(this, entity, m_selectedEntities.Count - 1));
                }
            }

            NotifyOfPropertyChange(() => CanAccept);
            HideLoading();
        }

        public new string Title
        {
            get { return !string.IsNullOrEmpty(m_name) ? m_name : "New group"; }
        }

        private string m_name;
        public string Name
        {
            get { return m_name; }
            set
            {
                if (m_name != value)
                {
                    m_name = value;
                    NotifyOfPropertyChange(() => Name);
                    NotifyOfPropertyChange(() => Title);
                    NotifyOfPropertyChange(() => CanAccept);
                }
            }
        }

        //private bool m_displayOnHomeScreen;
        //public bool DisplayOnHomeScreen
        //{
        //    get { return m_displayOnHomeScreen; }
        //    set
        //    {
        //        if(m_displayOnHomeScreen != value)
        //        {
        //            m_displayOnHomeScreen = value;
        //            NotifyOfPropertyChange(() => DisplayOnHomeScreen);
        //        }
        //    }
        //}

        private void LaunchSearch()
        {
            if (string.IsNullOrEmpty(m_searchTerms) && !m_selectedEntityType.SupportAllInOne())
            {
                m_keyboardTimer.Stop();
                return;
            }

            m_keyboardTimer.Stop();
            SearchInProgress = true;

            try
            {
                m_entities = new EntitiesCollection(this, new QueryDefinition(m_selectedQueryType, m_selectedEntityType), m_searchTerms, m_selectedEntities.Select(e => e.Entity), m_tokenSource.Token);
                m_entities.StartSearching += (s, e) =>
                {
                    m_inSearch = true;
                    SearchInProgress = true;
                    m_searchTimer.Start();
                };
                m_entities.EndSearching += (s, e) =>
                {
                    m_inSearch = false;
                    m_searchTimer.Start();
                };
                NotifyOfPropertyChange(() => Entities);
            }
            catch (Exception ex)
            {
                if (!m_tokenSource.IsCancellationRequested)
                    RedirectToError(ex);
            }

            SearchInProgress = false;
        }

        private DispatcherTimer m_keyboardTimer;
        private string m_searchTerms;
        public string SearchTerms
        {
            get { return m_searchTerms; }
            set
            {
                m_keyboardTimer.Stop();

                if(m_searchTerms != value)
                {
                    m_searchTerms = value;
                    NotifyOfPropertyChange(() => SearchTerms);

                    if (string.IsNullOrEmpty(m_searchTerms) && !m_selectedEntityType.SupportAllInOne())
                    {
                        m_keyboardTimer.Stop();
                    }
                    else
                    {
                        m_keyboardTimer = new DispatcherTimer();
                        m_keyboardTimer.Interval = TimeSpan.FromMilliseconds(1000);
                        m_keyboardTimer.Tick += (s, e) => LaunchSearch();
                        m_keyboardTimer.Start();
                    }
                }
            }
        }

        private bool m_searchInProgress;
        public bool SearchInProgress
        {
            get { return m_searchInProgress; }
            set
            {
                if(m_searchInProgress != value)
                {
                    m_searchInProgress = value;
                    NotifyOfPropertyChange(() => SearchInProgress);
                }
            }
        }

        private bool m_searchImageInProgress;
        public bool SearchImageInProgress
        {
            get { return m_searchImageInProgress; }
            set
            {
                if (m_searchImageInProgress != value)
                {
                    m_searchImageInProgress = value;
                    NotifyOfPropertyChange(() => SearchImageInProgress);
                }
            }
        }

        private DispatcherTimer m_imageKeyboardTimer;
        private string m_imageSearchTerms;
        public string ImageSearchTerms
        {
            get { return m_imageSearchTerms; }
            set
            {
                m_imageKeyboardTimer.Stop();
                m_searchedImages.Clear();

                if (m_imageSearchTerms != value)
                {
                    m_imageSearchTerms = value;
                    NotifyOfPropertyChange(() => ImageSearchTerms);

                    m_imageKeyboardTimer = new DispatcherTimer();
                    m_imageKeyboardTimer.Interval = TimeSpan.FromMilliseconds(1000);
                    m_imageKeyboardTimer.Tick += async (s, e) => await LaunchImageSearchAsync();
                    m_imageKeyboardTimer.Start();
                }
            }
        }

        private async Task LaunchImageSearchAsync()
        {
            if (string.IsNullOrEmpty(m_imageSearchTerms))
            {
                m_imageKeyboardTimer.Stop();
                return;
            }

            m_imageKeyboardTimer.Stop();
            SearchImageInProgress = true;

            try
            {
                m_searchedImages.Clear();
                var images = await new BingImageSearchEngine(Downloader.New).SearchImagesAsync(m_imageSearchTerms, m_tokenSource.Token);
                foreach (var image in images) m_searchedImages.Add(image);
            }
            catch { }

            SearchImageInProgress = false;
        }

        public BindableCollection<string> EntityTypes { get { return m_entityTypes; } }
        public BindableCollection<string> SearchedImages { get { return m_searchedImages; } }

        private void RefreshEntityTypes()
        {
            var selectedType = m_selectedEntityType;

            m_entityTypes.Clear();

            var typeSupported = new List<EntityType>();
            foreach(var definition in RepositoryBase.GetAllInstances().SelectMany(r => r.SupportedQueries))
            {
                if(!typeSupported.Contains(definition.EntityType) && definition.QueryType == m_selectedQueryType)
                {
                    typeSupported.Add(definition.EntityType);
                }
            }

            foreach (var entity in typeSupported)
                m_entityTypes.Add(entity.ToString());

            if (!typeSupported.Contains(selectedType)) m_selectedEntityType = typeSupported[0];
            else m_selectedEntityType = selectedType;

            NotifyOfPropertyChange(() => SelectedEntityType);
        }
        

        private EntityType m_selectedEntityType;
        public string SelectedEntityType
        {
            get { return m_selectedEntityType.ToString(); }
            set
            {
                if(m_selectedEntityType.ToString() != value)
                {
                    if (value != null)
                    {
                        m_selectedEntityType = (EntityType)Enum.Parse(typeof(EntityType), value);
                        NotifyOfPropertyChange(() => SelectedEntityType);
                        LaunchSearch();
                    }
                }
            }
        }

        private QueryType m_selectedQueryType;
        public QueryType SelectedQueryType
        {
            get { return m_selectedQueryType; }
            set
            {
                if(m_selectedQueryType != value)
                {
                    m_selectedQueryType = value;
                    NotifyOfPropertyChange(() => SelectedQueryType);
                    RefreshEntityTypes();
                }
            }
        }

        public async void CheckQueryType()
        {
            var unsupportedEntities = new List<EntityResultViewModel>();
            
            foreach(var entity in m_selectedEntities)
            {
                var queryDefinition = new QueryDefinition(m_selectedQueryType, entity.Entity.Type);

                var repo = RepositoryBase.GetInstance(entity.Entity.Repository);
                if(!repo.SupportQuery(queryDefinition))
                {
                    unsupportedEntities.Add(entity);
                }
                else if (repo.UnsupportedEntities != null && repo.UnsupportedEntities.ContainsKey(queryDefinition))
                {
                    var entities = repo.UnsupportedEntities[queryDefinition];
                    if(entities.Contains(entity.Entity.Id))
                    {
                        unsupportedEntities.Add(entity);
                    }
                }
            }

            if (unsupportedEntities.Count > 0)
            {
                var dialog = new MessageDialog(
                    string.Format("Some queries already registered don't support '{0}' query type." +
                                  "If you accept the modification, these queries will be deleted." +
                                  "Would you like to accept the modification ?",  m_selectedQueryType.ToString()),
                                  "Unsupported queries");
                dialog.Commands.Add(new UICommand("Yes", (s) =>
                    {
                        m_selectedEntities.RemoveRange(unsupportedEntities);
                        RefreshCommandBar();
                        RecalculateIndex();
                        NotifyOfPropertyChange(() => CanAccept);
                        LaunchSearch();
                    }));
                dialog.Commands.Add(new UICommand("No", (s) => SelectedQueryType = (m_selectedQueryType == QueryType.AllTracks ? QueryType.TopTracks : QueryType.AllTracks)));
                dialog.CancelCommandIndex = 1;
                await dialog.ShowAsync();
            }
            else
            {
                LaunchSearch();
            }
        }

        public void RefreshCommandBar()
        {
            if (m_entities != null)
            {
                ShowCommandBar = m_entities.Union(m_selectedEntities).Any(e => e.IsSelected);
                ShowAddButton = m_entities.Any(e => e.IsSelected);
                ShowRemoveButton = m_selectedEntities.Any(e => e.IsSelected);
            }
            else
            {
                ShowCommandBar = m_selectedEntities.Any(e => e.IsSelected);
                ShowAddButton = false;
                ShowRemoveButton = m_selectedEntities.Any(e => e.IsSelected);
            }
        }

        private void RecalculateIndex()
        {
            if(m_entities != null)
                for(int i = 0; i < m_entities.Count; i++)
                {
                    m_entities[i].Index = i;
                }

            if(m_selectedEntities != null)
                for (int i = 0; i < m_selectedEntities.Count; i++)
                {
                    m_selectedEntities[i].Index = i;
                }
        }

        private bool m_showAddButton;
        public bool ShowAddButton
        {
            get { return m_showAddButton; }
            set
            {
                if(m_showAddButton != value)
                {
                    m_showAddButton = value;
                    NotifyOfPropertyChange(() => ShowAddButton);
                }
            }
        }

        private bool m_showRemoveButton;
        public bool ShowRemoveButton
        {
            get { return m_showRemoveButton; }
            set
            {
                if (m_showRemoveButton != value)
                {
                    m_showRemoveButton = value;
                    NotifyOfPropertyChange(() => ShowRemoveButton);
                }
            }
        }

        private bool m_showCommandBar;
        public bool ShowCommandBar
        {
            get { return m_showCommandBar; }
            set
            {
                if(m_showCommandBar != value)
                {
                    m_showCommandBar = value;
                    NotifyOfPropertyChange(() => ShowCommandBar);
                }
            }
        }

        public EntitiesCollection Entities { get { return m_entities; } }
        public BindableCollection<EntityResultViewModel> SelectedEntities { get { return m_selectedEntities; } }

        private byte[] m_imageBytes;
        public byte[] ImageBytes { get { return m_imageBytes; } }

        private string m_imageUrl;
        public string ImageUrl
        {
            get { return m_imageUrl; }
            set
            {
                if(m_imageUrl != value)
                {
                    m_imageUrl = value;
                    NotifyOfPropertyChange(() => ImageUrl);
                    NotifyOfPropertyChange(() => CanUploadImage);
                }
            }
        }

        public bool CanUploadImage
        {
            get { return !string.IsNullOrEmpty(m_imageUrl);  }
        }

        public async void UploadImage()
        {
            try
            {
                UploadInProgress = true;
                m_imageBytes = await Downloader.Instance.GetOnlineDataAsync(m_imageUrl, new System.Threading.CancellationToken());
                if (m_image == null)
                {
                    m_image = new BitmapImage();
                    m_image.DecodePixelHeight = 170;
                }
                await Image.FillWithBytes(m_imageBytes);
                NotifyOfPropertyChange(() => Image);
            }
            catch
            {
                m_imageBytes = null;
                Image = null;
            }
            finally
            {
                UploadInProgress = false;
            }
        }

        private bool m_uploadInProgress;
        public bool UploadInProgress
        {
            get { return m_uploadInProgress; }
            set
            {
                if(m_uploadInProgress != value)
                {
                    m_uploadInProgress = value;
                    NotifyOfPropertyChange(() => UploadInProgress);
                }
            }
        }

        private BitmapImage m_image;
        public BitmapImage Image
        {
            get { return m_image; }
            set
            {
                if(m_image != value)
                {
                    m_image = value;
                    NotifyOfPropertyChange(() => Image);
                }
            }
        }

        public void AddSelectedEntities()
        {
            var entities = m_entities.Where(e => e.IsSelected).ToList();
            if (entities != null)
            {
                foreach (var entity in Entities)
                {
                    entity.IsSelected = false;
                }

                m_selectedEntities.AddRange(entities);
                m_entities.RemoveRange(entities);
            }

            RefreshCommandBar();
            RecalculateIndex();
            NotifyOfPropertyChange(() => CanAccept);
        }

        public void RemoveSelectedEntities()
        {
            var entities = m_selectedEntities.Where(e => e.IsSelected).ToList();
            if (entities != null)
            {
                m_selectedEntities.RemoveRange(entities);
            }

            RefreshCommandBar();
            RecalculateIndex();
            NotifyOfPropertyChange(() => CanAccept);
        }

        public bool CanAccept
        {
            get { return m_selectedEntities.Any() && !string.IsNullOrEmpty(m_name); }
        }

        public async void SelectImage(ItemClickEventArgs eventArgs)
        {
            UploadInProgress = true;
            var image = (string)eventArgs.ClickedItem;
            m_imageBytes = await Downloader.New.GetOnlineDataAsync(image, new System.Threading.CancellationToken());
            await Image.FillWithBytes(m_imageBytes);
            UploadInProgress = false;
        }

        public async void Accept()
        {
            ShowLoading();

            m_queryGroup.Name = m_name;
            m_queryGroup.QueryType = m_selectedQueryType;
            m_queryGroup.Image = m_imageBytes;
            //m_queryGroup.DisplayNameOnHomeScreen = m_displayOnHomeScreen;

            m_queryGroup.Entities.Clear();
            foreach(var entity in SelectedEntities)
            {
                m_queryGroup.Entities.Add(entity.Entity);
            }
            await ServiceDatabase.InsertOrUpdateQueryGroupAsync(m_queryGroup);

            m_navigationService.NavigateToViewModel<HomePageViewModel>(new NavigationObject()
            {
                Root = Root,
                SourcePage = null
            });

            HideLoading();
        }

        public void Cancel()
        {
            m_navigationService.NavigateToViewModel<HomePageViewModel>(new NavigationObject()
            {
                Root = Root,
                SourcePage = null
            });
        }
    }
}
